
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "i7816_config.h"
#include "i7816_log.h"
#include "i7816_utils.h"
#include "i7816_hal.h"

#define I_BLOCK_MARK	0x80
#define I_BLOCK_FLAG	0x00

#define R_BLOCK_MARK	0xC0
#define R_BLOCK_FLAG	0x80

#define S_BLOCK_MARK	0xC0
#define S_BLOCK_FLAG	0xC0

#define I_SN_BIT_NUM	6
#define I_CHAIN_BIT_NUM	5
#define R_SN_BIT_NUM	4
#define S_REQ_BIT_NUM	5

#define S_REQU			0
#define S_RESP			(1 << S_REQ_BIT_NUM)
#define S_TYPE_MARK		0x1F

#define S_RESYNC		0
#define S_IFS			1
#define S_ABORT			2
#define S_BWTX			3

#define T1_IFSD			0xFE

typedef struct
{
    uint8_t NAD;
    uint8_t PCB;
    uint8_t LEN;
    uint8_t *INF;
    uint8_t DEC;
    uint8_t inf_size;
} block_t;

static uint32_t is_I_block(block_t *block);
static uint32_t is_R_block(block_t *block);
static uint32_t is_S_block(block_t *block);
static uint8_t get_I_block_SN(block_t *block);
static uint8_t get_R_block_SN(block_t *block);

static void PrintBlock(const char* tag, block_t* block){
#if I7816_LOG_CFG_LEVEL
	char blockHead[16];
	if(is_I_block(block)){
		sprintf(blockHead, "I(%02X)", get_I_block_SN(block));
	}
	else
	if(is_R_block(block)){
		sprintf(blockHead, "R(%02X)", get_R_block_SN(block));
	}
	else
	if(is_S_block(block)){
		strcpy(blockHead, "S");
	}
	I7816_LOG_DBG("**************");
    I7816_LOG_BUF(tag, (uint8_t*)block, 3);
    I7816_LOG_BUF(blockHead, block->INF, block->LEN);
    I7816_LOG_DBG(" %02X", block->DEC);
    I7816_LOG_DBG("--------------");
#endif    
}

typedef struct
{
    uint8_t *c_apdu;
    uint32_t c_apdu_len;
    uint32_t out_offset;

    uint8_t *r_apdu;
    uint32_t in_offset;
    uint8_t error_countor;
} transmission_t;


static void init_nak_block(block_t *nak, uint8_t sn, uint32_t err);


static uint8_t get_block_LRC(block_t *block)
{
    uint8_t ck;

    ck = block->NAD;
    ck ^= block->PCB;
    ck ^= block->LEN;

    if(block->LEN > 0)
    {
        ck ^= i7816_xor_sequence(block->INF, block->LEN);
    }

    return ck;
}

static void init_block_send(block_t *block, uint8_t pcb, uint8_t inf[], uint8_t inf_length)
{
    block->NAD = 0;
    block->PCB = pcb;
    block->LEN = inf_length;
    block->INF = inf;
    block->DEC = get_block_LRC(block);
}

static void send_block(i7816_ifd_t *ifd, block_t *block)
{
    i7816_hw_t *hw = ifd->hw;
    /*
    Denoted BGT, the “block guard time” is the minimum delay between the leading edges of two consecutive 
    characters in opposite directions. BGT = 22 etu.
    */
	uint32_t clk = i7816_etu2clk(22 - 11, hw->param->FiDi);
    i7816_hal_delay_guard_time(hw,  clk);
    i7816_hal_setup(hw, TRANSFER_OUT);
    i7816_hal_send(hw, block->NAD);
    i7816_hal_send(hw, block->PCB);
    i7816_hal_send(hw, block->LEN);
    for(int i = 0; i < block->LEN; i++){
        i7816_hal_send(hw, block->INF[i]);
    }
    i7816_hal_send(hw, block->DEC);
    i7816_hal_flush(hw, TRANSFER_OUT);
}

static i7816_status_t i7816_t1_receive_buf(i7816_hw_t *hw, uint8_t* rxbuf, uint16_t len, uint32_t wait_time){
    i7816_status_t status = I7816_S_OK;
    uint16_t rdata;

    for(uint32_t i = 0; i < len; i++){
        status = i7816_hal_receive(hw, &rdata, wait_time);
        if(status != I7816_S_OK){
            return status;
        }
        rxbuf[i] = (uint8_t)rdata;
    }

    return I7816_S_OK;
}

static void init_block_receive(block_t *block, uint8_t inf[], uint8_t inf_size)
{
    memset(block, 0, sizeof(block_t));
    block->INF = inf;
    block->inf_size = inf_size;
}

static uint32_t dummy_receive(i7816_hw_t *hw, uint8_t len, uint32_t wait_time)
{
    uint16_t dummy;
    uint8_t i;
    i7816_status_t ret;
	i7816_status_t status = I7816_S_OK;
	
    for(i = 0; i < len; i++)
    {        
        ret = i7816_hal_receive(hw, &dummy, wait_time);

        if(ret != I7816_S_OK)
        {
        	if(!status)
        		status = ret;
        	if(status == I7816_S_TIMEOUT)
        	{
				I7816_LOG_ERR("rx dummy timeout(%u)!", wait_time);
            	break;
            }
        }
    }

    return status;
}

static uint32_t receive_block_inf(i7816_ifd_t* ifd, uint8_t inf[], uint8_t inf_len)
{
	uint32_t ret;
	uint32_t status = I7816_S_OK;
	uint8_t i;
	i7816_hw_t *hw = ifd->hw;
	uint32_t CWT = ifd->icc.CWT;
    uint16_t rdata;

	for(i = 0; i < inf_len; i++)
	{
		ret = i7816_t1_receive_buf(hw, inf + i, 1, CWT);
        if(ret != I7816_S_OK)
        {
        	I7816_LOG_ERR("rxinf: len=%d,i=%d, err=%d", inf_len, i, ret);
        	if(!status)
            	status = ret;

            if(ret == I7816_S_TIMEOUT)
            {
				I7816_LOG_ERR("rx INF timeout(%u)!", CWT);
            	break;
            }
        }
	}
	
	return status;
}

static uint32_t receive_block(i7816_ifd_t* ifd, block_t *block, uint32_t BWT)
{
    uint32_t ret;
    uint32_t status = I7816_S_OK;
    i7816_icc_t *icc = &ifd->icc;
    i7816_hw_t *hw = ifd->hw;
    uint32_t CWT = icc->CWT;
    uint16_t rdata;

    ret = i7816_t1_receive_buf(hw, &block->NAD, 1, BWT);    
    if(ret != I7816_S_OK)
    {
    	if(!status)
        	status = ret;

        if(ret == I7816_S_TIMEOUT)
        {
        	I7816_LOG_ERR("rx NAD timeout(%u)!", BWT);
            return status;
        }
    }

    ret = i7816_t1_receive_buf(hw, &block->PCB, 1, CWT);

    if(ret != I7816_S_OK)
    {
    	if(!status)
        	status = ret;

        if(ret == I7816_S_TIMEOUT)
        {
        	I7816_LOG_ERR("rx PCB timeout(%u)!", CWT);
            return status;
        }
    }

    ret = i7816_t1_receive_buf(hw, &block->LEN, 1, CWT);

    if(ret != I7816_S_OK)
    {
    	if(!status)
        	status = ret;

        if(ret == I7816_S_TIMEOUT)
        {
        	I7816_LOG_ERR("rx LEN timeout(%u)!", CWT);
            return status;
        }
    }

    if(block->LEN > 0)
    {
        uint8_t rx_len = (uint8_t)i7816_min(block->LEN, block->inf_size);
        
        ret = receive_block_inf(ifd, block->INF, rx_len);

        if(ret != I7816_S_OK)
        {
			if(!status)
				status = ret;

            if(ret == I7816_S_TIMEOUT)
            {
				I7816_LOG_ERR("rx INF timeout!");
               return status;
            }
        }

        if(block->LEN > rx_len)
        {
            ret = dummy_receive(hw, block->LEN - rx_len, CWT);
			I7816_LOG_ERR("rx overload!");
            if(ret == I7816_S_TIMEOUT)
            {
                status = ret;
                return status;
            }
			if(!status){
				status = I7816_S_BLOCK_FORMAT;
            }
        }
    }

    ret = i7816_t1_receive_buf(hw, &block->DEC, 1, CWT*2);

    if(ret != I7816_S_OK)
    {
		if(!status)
			status = ret;

        if(ret == I7816_S_TIMEOUT)
        {
			I7816_LOG_ERR("rx DEC timeout(%u)!", CWT);
            return status;
        }
    }

    return status;
}

static uint32_t check_block_DEC(block_t *block)
{
    uint8_t dec;

    dec = get_block_LRC(block);

    return dec == block->DEC;
}



static uint8_t get_I_block_SN(block_t *block)
{
    return I7816_BIT_GET(block->PCB, I_SN_BIT_NUM);
}

static uint8_t get_R_block_SN(block_t *block)
{
    return I7816_BIT_GET(block->PCB, R_SN_BIT_NUM);
}

static uint32_t is_chain_block(block_t *block)
{
    return (block->PCB & (1 << I_CHAIN_BIT_NUM)) != 0;
}

static uint32_t is_I_block(block_t *block)
{
    return (block->PCB & I_BLOCK_MARK) == I_BLOCK_FLAG;
}

static uint32_t is_R_block(block_t *block)
{
    return (block->PCB & R_BLOCK_MARK) == R_BLOCK_FLAG;
}

static uint32_t is_S_block(block_t *block)
{
    return (block->PCB & S_BLOCK_MARK) == S_BLOCK_FLAG;
}

static uint32_t is_valid_PCB_of_I_block(unsigned char pcb)
{
    return ((pcb & I_BLOCK_MARK) == I_BLOCK_FLAG) && ((pcb & 0x1F) == 0);
}

static uint32_t is_valid_PCB_of_R_block(unsigned char pcb)
{
    return ((pcb & R_BLOCK_MARK) == R_BLOCK_FLAG) && ((pcb & 0x23) < 0x03);
}

static uint32_t is_valid_PCB_of_S_block(unsigned char pcb)
{
    if((pcb & S_BLOCK_MARK) != S_BLOCK_FLAG)
    {
        return 0;
    }

    return ((pcb & S_TYPE_MARK) < 0x04) ;
}

static uint32_t is_PCB_valid(block_t *block)
{
    uint8_t pcb = block->PCB;

    return is_valid_PCB_of_I_block(pcb)
           || is_valid_PCB_of_R_block(pcb)
           || is_valid_PCB_of_S_block(pcb);
}

static uint32_t is_LEN_valid(block_t *block)
{
    uint8_t len = block->LEN;


    if(is_I_block(block))
    {
    /*
	This specification does not support I-blocks with LEN = 0. 
    */
        return /*0 < len &&*/ len <= T1_IFSD;
    }

    if(is_R_block(block))
    {
        return len == 0;
    }

    if(is_S_block(block))
    {
        return len < 2;
    }

    return 0;
}

static uint32_t is_NAD_valid(block_t *block)
{
/*
The first block sent by the terminal following the ATR and all follo wing blocks transmitted by either the 
terminal or ICC shall have the NAD = '00'.If during the card session the terminal  or ICC receives a block with a NAD  != '00', 
it may treat the block as invalid.
*/
    return block->NAD == 0;
}

static uint32_t is_block_format_correct(block_t *block)
{
    return is_PCB_valid(block)
           && is_LEN_valid(block)
           && is_NAD_valid(block);
}

static uint32_t block_exchange(i7816_ifd_t *ifd, block_t *block_send, block_t *block_receive, uint32_t BWT)
{
    uint32_t ret;
	block_receive->PCB = block_receive->NAD = block_receive->LEN = 0;
	PrintBlock("tx", block_send);
    send_block(ifd, block_send);
    ret = receive_block(ifd, block_receive, BWT);

    if(ret == I7816_S_OK)
    {
    	PrintBlock("rx", block_receive);
    	if(!check_block_DEC(block_receive))
    	{
			return I7816_S_DEC;
    	}
    	
        if(!is_block_format_correct(block_receive))
        {
            ret = I7816_S_BLOCK_FORMAT;
        }
    }

	if(ret){
		I7816_LOG_ERR("exchange fail %d", ret);
		PrintBlock("fail", block_receive);
	}
    return ret;
}

static uint32_t is_S_request_block(block_t *block)
{
    return is_S_block(block) && (I7816_BIT_GET(block->PCB, S_REQ_BIT_NUM) == 0u);
}

static uint32_t is_S_response_block(block_t *block)
{
    return is_S_block(block) && (I7816_BIT_GET(block->PCB, S_REQ_BIT_NUM) == 1u);
}

static uint8_t get_S_request_type(block_t *block)
{
    return block->PCB & S_TYPE_MARK;
}

//static uint32_t is_S_request_support(uint8_t request_type)
//{
//    return request_type == S_IFS || request_type == S_BWTX || request_type == S_ABORT;
//}

static uint32_t is_R_SN_EQ_SN_out(block_t *block, i7816_icc_t *icc)
{
    return get_R_block_SN(block) == (icc->SN_out & 0x01);
}

static uint32_t is_I_SN_valid(block_t *block, i7816_icc_t *icc)
{
/*
	I-blocks are numbered using a modulo-2 number coded on one bit.
*/
    return get_I_block_SN(block) == (icc->SN_in & 0x01);
}

static void set_S_direction(uint8_t *PCB, uint8_t dir)
{
    if(dir == S_REQU)
    {
        I7816_BIT_CLR(*PCB, S_REQ_BIT_NUM);
    }
    else
    {
    	I7816_BIT_SET(*PCB, S_REQ_BIT_NUM);        
    }
}

//static void set_S_type(uint8_t *PCB, uint8_t type)
//{
//    *PCB &= (~S_TYPE_MARK);
//    *PCB |= (type & S_TYPE_MARK);
//}

static void init_S_block(block_t *block, uint8_t PCB,
                         uint8_t inf[], uint8_t inf_len)
{
    init_block_send(block, PCB, inf, inf_len);
}

static void set_R_block_SN(uint8_t *PCB, uint8_t SN_in)
{
    if(SN_in & 0x01)
    {
        I7816_BIT_SET(*PCB, R_SN_BIT_NUM);
    }
    else
    {
        I7816_BIT_CLR(*PCB, R_SN_BIT_NUM);
    }
}

static void init_R_block(block_t *block, uint8_t SN_in)
{
    uint8_t PCB = R_BLOCK_FLAG;

    set_R_block_SN(&PCB, SN_in);
    init_block_send(block, PCB, (uint8_t *)0, 0);
}


static void set_I_block_SN(uint8_t *PCB, uint8_t SN)
{
    if(SN & 0x01)
    {
        I7816_BIT_SET(*PCB, I_SN_BIT_NUM);
    }
    else
    {
        I7816_BIT_CLR(*PCB, I_SN_BIT_NUM);
    }
}

static void set_I_block_chain(uint8_t *PCB, uint8_t is_chain)
{
    if(is_chain)
    {
        I7816_BIT_SET(*PCB, I_CHAIN_BIT_NUM);
    }
    else
    {
        I7816_BIT_CLR(*PCB, I_CHAIN_BIT_NUM);
    }
}

static void init_I_block(block_t *block, i7816_icc_t *icc, transmission_t *transmission)
{
    uint8_t PCB = I_BLOCK_FLAG;
    uint32_t remain_data_len = transmission->c_apdu_len - transmission->out_offset;
    uint32_t inf_len = (uint8_t)i7816_min(icc->IFSC, remain_data_len);
    uint8_t *inf = transmission->c_apdu + transmission->out_offset;

    set_I_block_SN(&PCB, icc->SN_out);
    set_I_block_chain(&PCB, remain_data_len > inf_len);
    init_block_send(block, PCB, inf, inf_len);
}
static uint32_t is_response_type_valid(block_t *request_block, block_t *response_block)
{
    return get_S_request_type(request_block) == get_S_request_type(response_block);
}

static uint32_t is_response_inf_valid(block_t *request_block, block_t *response_block)
{
    if(request_block->LEN == response_block->LEN)
    {
        if(request_block->LEN == 0)
        {
            return 1;
        }

        return memcmp(request_block->INF, response_block->INF, request_block->LEN) == 0;
    }

    return 0;
}

static uint32_t is_response_valid(block_t *request_block, block_t *response_block)
{
    return is_S_response_block(response_block)
           && is_response_type_valid(request_block, response_block)
           && is_response_inf_valid(request_block, response_block);
}


static uint32_t request_session(i7816_ifd_t* ifd, uint8_t PCB, uint8_t req_inf[], uint8_t req_inf_len)
{
    uint32_t ret;
    uint32_t error_count;
    block_t s_request_block;
    block_t s_response_block;
    uint8_t resp_inf[2];

    init_S_block(&s_request_block, PCB, req_inf, req_inf_len);
    init_block_receive(&s_response_block, resp_inf, req_inf_len);

    for(error_count = 0; error_count < 3; error_count++)
    {
        ret = block_exchange(ifd, &s_request_block, &s_response_block, ifd->icc.BWT);

        if(ret == I7816_S_OK)
        {
            if(is_response_valid(&s_request_block, &s_response_block))
            {
                break;
            }
            else
            {
            	
                ret = I7816_S_RESPONSE;
            }
        }
    }

    return ret;
}

uint32_t I7816_resync_request(i7816_ifd_t* ifd)
{
    uint8_t PCB = S_BLOCK_MARK | S_REQU | S_RESYNC;
    uint32_t ret;

    ret = request_session(ifd, PCB, 0, 0);

    if(ret == I7816_S_OK)
    {
        ifd->icc.SN_in = 0;
        ifd->icc.SN_out = 0;
    }

    return ret;
}


uint32_t I7816_ifsd_request(i7816_ifd_t* ifd)
{
    uint8_t PCB = S_BLOCK_MARK | S_REQU | S_IFS;
    uint8_t IFS = T1_IFSD;
    uint32_t ret;
    uint32_t i;

    for(i = 0; i < 3; i++)
    {
        ret = request_session(ifd, PCB, &IFS, sizeof(IFS));

        if(ret == I7816_S_OK)
        {
            ifd->icc.IFSD = IFS;
            break;
        }

        ret = I7816_resync_request(ifd);

        if(ret != I7816_S_OK)
        {
            break;
        }
    }

    return ret;
}


static inline uint32_t is_valid_IFSC(uint8_t IFSC)
{
    return (0x10 <= IFSC) && (IFSC <= 0xFE);
}

static uint32_t process_request(i7816_ifd_t* ifd, block_t *block_send, block_t *request_block, uint8_t *bwtx)
{
    uint32_t ret = I7816_S_OK;
    uint8_t request_type = get_S_request_type(request_block);
    i7816_icc_t *icc = &ifd->icc;

    switch(request_type)
    {
        case S_IFS:
        {
            uint8_t IFSC = request_block->INF[0];

            if(request_block->LEN == 1 && is_valid_IFSC(IFSC))
            {
                icc->IFSC = IFSC;
            }
            else
            {
                ret = I7816_S_REQUEST;
            }
        }
        break;

        case S_BWTX:
        {
            if(request_block->LEN == 1)
            {
            	uint8_t mult = request_block->INF[0];
                if(mult > 0)
                {
					*bwtx = mult;
                }
				else{
	                *bwtx = 1;
				}
            }
            else
            {
                ret = I7816_S_REQUEST;
            }
        }
        break;

        case S_ABORT:
        {
			ret = I7816_S_ABORT;
        }
        break;

        default:
        {
            ret = I7816_S_REQUEST;
        }
    }

    return ret;
}

static void set_response_block(block_t *response, block_t *request)
{
	uint8_t PCB;

	*response = *request;
	PCB = request->PCB;
	set_S_direction(&PCB, S_RESP);
	init_block_send(response, PCB, request->INF, request->LEN);
}

/*Response the S-Request*/
static uint32_t response_S_block(i7816_ifd_t* ifd, block_t *block_send, block_t *block_receive)
{
    uint32_t ret;
    block_t block_response;
    i7816_icc_t *icc = &ifd->icc;
    uint8_t bwtx;

    do
    {
        bwtx = 1;
        ret = process_request(ifd, block_send, block_receive, &bwtx);

        if(ret != I7816_S_OK)
        {
            break;
        }

        icc->error_countor = 0;
        set_response_block(&block_response, block_receive);
        ret = block_exchange(ifd, &block_response, block_receive, ifd->icc.BWT * bwtx);
        if(ret != I7816_S_OK)
        {
            break;
        }
    }
    while(is_S_request_block(block_receive));

    return ret;
}

static uint32_t process_response_for_chain_I_block(i7816_icc_t *icc,
        block_t *i_block, block_t *response_block)
{
    if(is_R_block(response_block))
    {
    	/*
		Chain_I_Block should be responsed by R-block
    	*/
        if(is_R_SN_EQ_SN_out(response_block, icc))
        {
            /*
			ICC expect previous block
            */
            return I7816_S_RETRANSMIT;
        }
        else
        {
        	/*
			ICC expect next block
        	*/
            return I7816_S_CORRECT;
        }
    }
    else
    {
    	/*
		unexpect block received
    	*/
        return I7816_S_TRANSMIT_R;
    }
}

static uint32_t process_response_for_nonchain_I_block(i7816_icc_t *icc,
        block_t *i_block, block_t *response_block)
{
    if(is_I_block(response_block)
            && is_I_SN_valid(response_block, icc))
    {
    	/*
		nonchain_I_block should be responsed by I_block
    	*/
    	if(response_block->LEN > 0) 
    	{
        	return I7816_S_CORRECT;
        }
        else
        {
			return I7816_S_TRANSMIT_R;
        }
    }

    if( is_R_block(response_block))
    {
    /*
		ICC expect previous block
    */
    	if(is_R_SN_EQ_SN_out(response_block, icc))
    	{
    		return I7816_S_RETRANSMIT;
    	}
    	else
    	{
    		/*error notification for first I-block ,ack*/
    		icc->error_countor = 0;
			return I7816_S_TRANSMIT_R;
    	}
    }
    else
    {
        return I7816_S_TRANSMIT_R;
    }

}

static uint32_t process_response_for_I_block(i7816_icc_t *icc,
        block_t *i_block, block_t *response_block)
{
    uint32_t ret;

    if(is_chain_block(i_block))
    {
        ret = process_response_for_chain_I_block(icc, i_block, response_block);
    }
    else
    {
        ret = process_response_for_nonchain_I_block(icc, i_block, response_block);
    }

    return ret;
}

static uint32_t process_response_for_R_block(i7816_icc_t *icc, block_t *response_block)
{
/*
	The terminal send a R-Block, expect an I-Block.
*/
    if(is_I_block(response_block)
            && is_I_SN_valid(response_block, icc))
    {
        return I7816_S_CORRECT;
    }
    else
    {
    /*
	ICC repsonse is unexpect.
    */
        return I7816_S_TRANSMIT_R;
    }
}

static void error_handle(uint32_t status, uint32_t error, i7816_icc_t *icc, block_t *retransmit, block_t *block_send)
{
    switch(status)
    {
        case I7816_S_RETRANSMIT:
        {
            *retransmit = *block_send;
        }
        break;

        case I7816_S_TRANSMIT_R:
        {
            //init_R_block(retransmit, icc->SN_in);
            init_nak_block(retransmit, icc->SN_in, error);
        }
        break;

        case I7816_S_RESYNCHRONIZE:
        {

        }
        break;
    }
}

static uint32_t information_transfer(i7816_ifd_t* ifd, block_t *block_send, block_t *block_receive)
{
    uint32_t ret;
    uint32_t status;
    block_t temp_block = *block_send;    
    i7816_icc_t *icc = &ifd->icc;
    uint32_t BWT = icc->BWT;

    icc->error_countor = 0;

    do
    {
        ret = block_exchange(ifd, &temp_block, block_receive, BWT);

        if(ret == I7816_S_OK)
        {
            if(is_S_request_block(block_receive))
            {
                ret = response_S_block(ifd, block_send, block_receive);

                if(ret != I7816_S_OK)
                {                    
					if(ret == I7816_S_ABORT)
					{
						return ret;
					}
                    icc->error_countor++;
                    if(icc->error_countor < 3)
                    {
                    	init_nak_block(&temp_block, icc->SN_in, ret);
                    	continue;
                    }
                    else
                    {
						break;
                    }
                }
            }

            if(is_R_block(block_send))
            {
                status = process_response_for_R_block(icc, block_receive);
            }
            else if(is_I_block(block_send))
            {
                status = process_response_for_I_block(icc, block_send, block_receive);
            }
            else
            {
                return I7816_S_INVALID_BLOCK;
            }
        }
        else
        {
            status = I7816_S_TRANSMIT_R;
        }

        if(status == I7816_S_CORRECT)
        {
            return I7816_S_OK;
        }

        error_handle(status, ret, icc, &temp_block, block_send);
        icc->error_countor++;
    }
    while(icc->error_countor < 3);

    return I7816_S_OVER_RETRANSMIT;
}

static uint32_t information_transfer_out(i7816_ifd_t* ifd, transmission_t *transmission)
{
    uint32_t ret;
    i7816_icc_t *icc = &ifd->icc;
    block_t block_send, block_receive;

    do
    {
        init_I_block(&block_send, icc, transmission);
        init_block_receive(&block_receive, transmission->r_apdu + transmission->in_offset, T1_IFSD);
        ret = information_transfer(ifd, &block_send, &block_receive);

        if(ret != I7816_S_OK)
        {
            return ret;
        }

        transmission->out_offset += block_send.LEN;
        icc->SN_out++;
        if(is_I_block(&block_receive))
        {
			transmission->in_offset += block_receive.LEN;
			icc->SN_in++;
			if(transmission->in_offset > 1024)
			{
				return I7816_S_OVER_BUFFER_SIZE;
			}
        }
    }
    while(transmission->c_apdu_len > transmission->out_offset);


    if(is_chain_block(&block_receive))
    {
        ret = I7816_S_CHAIN_IN;
    }

    return ret;
}

//static void init_null_block(block_t *block, i7816_icc_t *icc)
//{
//    uint8_t PCB = I_BLOCK_FLAG;
//
//    set_I_block_SN(&PCB, icc->SN_out);
//	set_I_block_chain(&PCB, 0);
//    init_block_send(block, PCB, (uint8_t *)0, 0);
//}

static void init_ack_block(block_t *block_send, i7816_icc_t *icc)
{
	init_R_block(block_send, icc->SN_in);
}

static void init_nak_block(block_t *nak, uint8_t sn, uint32_t err)
{
	init_R_block(nak, sn);
	if(err == I7816_S_PARITY || err == I7816_S_DEC)
	{
		nak->PCB |= 1; //DEC and/or parity error
	}
	else
	{
		nak->PCB |= 2;//Other error
	}
	nak->DEC = get_block_LRC(nak);
}

static uint32_t information_transfer_in(i7816_ifd_t* ifd, transmission_t *transmission)
{
    uint32_t ret = I7816_S_OK;
    i7816_icc_t *icc = &ifd->icc;
    block_t block_send, block_receive;

    while(transmission->in_offset < 1024)
    {
        init_ack_block(&block_send, icc);
        init_block_receive(&block_receive, transmission->r_apdu + transmission->in_offset, T1_IFSD);
        ret = information_transfer(ifd, &block_send, &block_receive);

        if(ret != I7816_S_OK)
        {
            return ret;
        }

        transmission->in_offset += block_receive.LEN;
        icc->SN_in++;

        if(!is_chain_block(&block_receive))
        {
        	return ret;
        }
    }

    return I7816_S_OVER_BUFFER_SIZE;
}

static i7816_status_t information_exchange(i7816_ifd_t* ifd, transmission_t *transmission)
{
    i7816_status_t ret;

    do
    {
        ret = information_transfer_out(ifd, transmission);
        if(ret != I7816_S_OVER_RETRANSMIT)
        {
            break;
        }

        ret = I7816_resync_request(ifd);

        if(ret != I7816_S_OK)
        {
            return ret;
        }
    }
    while(1);

    if(ret == I7816_S_CHAIN_IN)
    {
        do
        {
            ret = information_transfer_in(ifd, transmission);
            if(ret != I7816_S_OVER_RETRANSMIT)
            {
                break;
            }

            ret = I7816_resync_request(ifd);

            if(ret != I7816_S_OK)
            {
                return ret;
            }
        }
        while(1);

    }

    return ret;
}

static void Transmission_Init(transmission_t *transmission, uint8_t c_apdu[], uint32_t c_apdu_len, uint8_t r_apdu[])
{
    transmission->c_apdu = c_apdu;
    transmission->c_apdu_len = c_apdu_len;
    transmission->r_apdu = r_apdu;
    transmission->out_offset = 0;
    transmission->in_offset = 0;
    transmission->error_countor = 0;
}

i7816_status_t i7816_t1_exchange(i7816_ifd_t* ifd, uint8_t c_apdu[], uint16_t c_apdu_len,
                                    uint8_t r_apdu[], uint16_t *r_apdu_len)
{
    i7816_status_t ret = I7816_S_OK;    
    transmission_t transmission;
    if(ifd->icc.IFSD == 0){
        ret = I7816_ifsd_request(ifd);
        if(ret != I7816_S_OK){
            return ret;
        }
    }

    Transmission_Init(&transmission, c_apdu, c_apdu_len, r_apdu);
    ret = information_exchange(ifd, &transmission);

    if(ret == I7816_S_OK)
    {
        *r_apdu_len = transmission.in_offset;
    }

    return ret;
}


